package com.xwltz.common.file.cos;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.AnonymousCOSCredentials;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.BasicSessionCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.UploadResult;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.transfer.TransferManager;
import com.qcloud.cos.transfer.TransferManagerConfiguration;
import com.qcloud.cos.transfer.Upload;
import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Response;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @description CosUtils
 * @information on version 1.0 be created by @xwltz at 2022/1/18 14:25
 */

@Component
public class CosUtils {

	@Value("cos.secretId")
	protected String secretId;

	@Value("cos.secretKey")
	protected String secretKey;

	@Value("cos.region")
	protected String region;

	@Value("cos.bucket")
	protected String bucket;

	public static CosUtils Instance;

	// 公开实例
	public CosUtils() {
		Instance = new CosUtils();
	}

	// 创建 COSClient 实例，这个实例用来后续调用请求
	COSClient getCosClient() {
		// 设置用户身份信息。
		// SECRET ID 和 SECRET KEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理
		COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);

		// 生成 cos 客户端。
		return new COSClient(cred, getClientConfig());
	}

	// 获取 客户端配置
	ClientConfig getClientConfig() {
		// ClientConfig 中包含了后续请求 COS 的客户端设置：
		ClientConfig clientConfig = new ClientConfig();

		// 设置 bucket 的地域
		// COS_REGION 请参照 https://cloud.tencent.com/document/product/436/6224
		clientConfig.setRegion(new Region(region));

		// 设置请求协议, http 或者 https
		// 5.6.53 及更低的版本，建议设置使用 https 协议
		// 5.6.54 及更高版本，默认使用了 https
		clientConfig.setHttpProtocol(HttpProtocol.https);

		// 以下的设置，是可选的：

		// 设置 socket 读取超时，默认 30s
		clientConfig.setSocketTimeout(30 * 1000);
		// 设置建立连接超时，默认 30s
		clientConfig.setConnectionTimeout(30 * 1000);

		// 如果需要的话，设置 http 代理，ip 以及 port
		// clientConfig.setHttpProxyIp("httpProxyIp");
		// clientConfig.setHttpProxyPort(80);

		return clientConfig;
	}

	// 获取 临时秘钥
	Response getCredential() {
		TreeMap<String, Object> config = new TreeMap<>();

		try {
			// 云 api 密钥 SecretId
			config.put("secretId", secretId);
			// 云 api 密钥 SecretKey
			config.put("secretKey", secretKey);
			// 临时密钥有效时长，单位是秒
			config.put("durationSeconds", 1800);
			// 换成你的 bucket
			config.put("bucket", bucket);
			// 换成 bucket 所在地区
			config.put("region", region);

			return CosStsClient.getCredential(config);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	// 创建 TransferManager 实例，这个实例用来后续调用高级接口
	TransferManager getTransferManager() {
		// 创建一个 COSClient 实例，这是访问 COS 服务的基础实例。
		// 详细代码参见本页: 简单操作 -> 创建 COSClient
		COSClient cosClient = getCosClient();

		// 自定义线程池大小，建议在客户端与 COS 网络充足（例如使用腾讯云的 CVM，同地域上传 COS）的情况下，设置成16或32即可，可较充分的利用网络资源
		// 对于使用公网传输且网络带宽质量不高的情况，建议减小该值，避免因网速过慢，造成请求超时。
		ExecutorService threadPool = Executors.newFixedThreadPool(32);

		// 传入一个 thread pool, 若不传入线程池，默认 TransferManager 中会生成一个单线程的线程池。
		TransferManager transferManager = new TransferManager(cosClient, threadPool);

		// 设置高级接口的配置项
		// 分块上传阈值和分块大小分别为 5MB 和 1MB
		TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
		transferManagerConfiguration.setMultipartUploadThreshold(5 * 1024 * 1024);
		transferManagerConfiguration.setMinimumUploadPartSize(1024 * 1024);
		transferManager.setConfiguration(transferManagerConfiguration);

		return transferManager;
	}

	// 关闭 TransferManager 实例
	void shutdownTransferManager(TransferManager transferManager) {
		// 指定参数为 true, 则同时会关闭 transferManager 内部的 COSClient 实例。
		// 指定参数为 false, 则不会关闭 transferManager 内部的 COSClient 实例。
		transferManager.shutdownNow(true);
	}

	// 上传 文件
	public String putObject(String bucketName, String key, File file) {
		// 使用高级接口必须先保证本进程存在一个 TransferManager 实例，如果没有则创建
		// 详细代码参见本页：高级接口 -> 创建 TransferManager
		TransferManager transferManager = getTransferManager();
		try {
			PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, file);
			// 高级接口会返回一个异步结果Upload
			// 可同步地调用 waitForUploadResult 方法等待上传完成，成功返回UploadResult, 失败抛出异常
			Upload upload = transferManager.upload(putObjectRequest);
			UploadResult uploadResult = upload.waitForUploadResult();
			return uploadResult.getKey();
		} catch (CosClientException | InterruptedException e) {
			e.printStackTrace();
			return "";
		} finally {
			// 确定本进程不再使用 transferManager 实例之后，关闭之
			// 详细代码参见本页：高级接口 -> 关闭 TransferManager
			shutdownTransferManager(transferManager);
		}
	}

	// 查询 对象存储是否存在
	public Boolean Exist(String bucketName, String key) {
		// 调用 COS 接口之前必须保证本进程存在一个 COSClient 实例，如果没有则创建
		// 详细代码参见本页：简单操作 -> 创建 COSClient
		COSClient cosClient = getCosClient();
		// 存储桶的命名格式为 BucketName-APPID，此处填写的存储桶名称必须为此格式
		// 对象键(Key)是对象在存储桶中的唯一标识。详情请参见 [对象键](https://cloud.tencent.com/document/product/436/13324)
		try {
			return cosClient.doesObjectExist(bucketName, key);
		} catch (CosClientException e) {
			e.printStackTrace();
			return false;
		} finally {
			// 确认本进程不再使用 cosClient 实例之后，关闭之
			cosClient.shutdown();
		}
	}

	// 获取 文件URL 公共读
	public String getObjectByPublic(String bucketName, String key) {
		// 不需要验证身份信息
		COSCredentials cred = new AnonymousCOSCredentials();

		// 生成cos客户端
		COSClient cosclient = new COSClient(cred, getClientConfig());

		// 存储桶的命名格式为 BucketName-APPID，此处填写的存储桶名称必须为此格式
		// 对象键(Key)是对象在存储桶中的唯一标识。详情请参见 [对象键](https://cloud.tencent.com/document/product/436/13324)

		var result = cosclient.getObjectUrl(bucketName, key);
		return result.getPath();
	}

	// 获取 文件URL 私有读
	public String getObjectByPrivate(String bucketName, String key) {
		var credential = getCredential();
		COSCredentials cred = new BasicSessionCredentials(credential.credentials.tmpSecretId, credential.credentials.tmpSecretKey, credential.credentials.sessionToken);

		// 设置签名过期时间(可选), 若未进行设置则默认使用 ClientConfig 中的签名过期时间(1小时)
		// 这里设置签名在半个小时后过期
		Date expirationDate = new Date(System.currentTimeMillis() + 30 * 60 * 1000);

		// 填写本次请求的参数，需与实际请求相同，能够防止用户篡改此签名的 HTTP 请求的参数
		Map<String, String> params = new HashMap<>();

		// 填写本次请求的头部，需与实际请求相同，能够防止用户篡改此签名的 HTTP 请求的头部
		Map<String, String> headers = new HashMap<>();

		// 请求的 HTTP 方法，上传请求用 PUT，下载请求用 GET，删除请求用 DELETE
		HttpMethodName method = HttpMethodName.GET;

		// 生成cos客户端
		COSClient cosClient = new COSClient(cred, getClientConfig());

		try {
			// 存储桶的命名格式为 BucketName-APPID，此处填写的存储桶名称必须为此格式
			// 对象键(Key)是对象在存储桶中的唯一标识。详情请参见 [对象键](https://cloud.tencent.com/document/product/436/13324)
			URL url = cosClient.generatePresignedUrl(bucketName, key, expirationDate, method, headers, params);
			return url.getPath();
		} catch (CosClientException e) {
			e.printStackTrace();
			return "";
		} finally {
			// 确认本进程不再使用 cosClient 实例之后，关闭之
			cosClient.shutdown();
		}
	}
}
